package bsh.utils.reflection;

import java.util.HashMap;
import java.util.Map;

import bsh.compilation.CompilationResults;

/**
 * @author takeshi
 */
public class OpenClassLoader extends ClassLoader {

    private final Map<String, byte[]> typesByName = new HashMap<String, byte[]>();

    /**
     * C'tor que usa o classloader do 'OpenClassLoader' como parent classloader
     */
    public OpenClassLoader() {
        super(OpenClassLoader.class.getClassLoader());
    }

    /**
     * C'tor que seleciona o parent classloader
     * 
     * @param parent {@link ClassLoader}
     */
    public OpenClassLoader(ClassLoader parent) {
        super(parent);
    }

    /**
     * @param name String
     * @param data byte
     * @return class
     */
    public Class<?> defineClass(String name, byte[] data) {
        return super.defineClass(name, data, 0, data.length);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected synchronized Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = typesByName.remove(name);
        if (data == null) {
            throw new ClassNotFoundException(name);
        }
        return defineClass(name, data);
    }

    /**
     * Loads all the classes from the given {@link CompilationResults}
     * 
     * @param results {@link CompilationResults}
     * @return {@link Class}
     */
    public synchronized Class<?> load(CompilationResults results) {
        this.typesByName.putAll(results.getDependentTypes());
        return defineClass(results.getInternalName().replaceAll("/", "."), results.getBytecode());
    }

    /**
     * Register a class to be loaded
     * 
     * @param internalName the class name
     * @param bytecode the bytecode
     */
    public synchronized void registerClass(String internalName, byte[] bytecode) {
        this.typesByName.put(internalName.replaceAll("/", "."), bytecode);
    }

}
